{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<i>Copyright (c) Microsoft Corporation. All rights reserved.</i>\n",
    "\n",
    "<i>Licensed under the MIT License.</i>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Sequential Recommender Quick Start\n",
    "\n",
    "### Example: SLi_Rec : Adaptive User Modeling with Long and Short-Term Preferences for Personailzed Recommendation\n",
    "Unlike a general recommender such as Matrix Factorization or xDeepFM (in the repo) which doesn't consider the order of the user's activities, sequential recommender systems take the sequence of the user behaviors as context and the goal is to predict the items that the user will interact in a short time (in an extreme case, the item that the user will interact next).\n",
    "\n",
    "This notebook aims to give you a quick example of how to train a sequential model based on a public Amazon dataset. Currently, we can support GRU4Rec \\[2\\], Caser \\[3\\], A2SVD \\[1\\] and SLi_Rec \\[1\\]. Without loss of generality, this notebook takes [SLi_Rec model](https://www.microsoft.com/en-us/research/uploads/prod/2019/07/IJCAI19-ready_v1.pdf) for example.\n",
    "SLi_Rec \\[1\\] is a deep learning-based model aims at capturing both long and short-term user preferences for precise recommender systems. To summarize, SLi_Rec has the following key properties:\n",
    "\n",
    "* It adopts the attentive \"Asymmetric-SVD\" paradigm for long-term modeling;\n",
    "* It takes both time irregularity and semantic irregularity into consideration by modifying the gating logic in LSTM.\n",
    "* It uses an attention mechanism to dynamic fuse the long-term component and short-term component.\n",
    "\n",
    "In this notebook, we test SLi_Rec on a subset of the public dataset: [Amazon_reviews](http://snap.stanford.edu/data/amazon/productGraph/categoryFiles/reviews_Movies_and_TV_5.json.gz) and [Amazon_metadata](http://snap.stanford.edu/data/amazon/productGraph/categoryFiles/meta_Movies_and_TV.json.gz)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 0. Global Settings and Imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "System version: 3.6.8 |Anaconda, Inc.| (default, Feb 21 2019, 18:30:04) [MSC v.1916 64 bit (AMD64)]\n",
      "Tensorflow version: 1.12.0\n"
     ]
    }
   ],
   "source": [
    "import sys\n",
    "sys.path.append(\"../../\")\n",
    "import os\n",
    "import logging\n",
    "import papermill as pm\n",
    "from tempfile import TemporaryDirectory\n",
    "\n",
    "import tensorflow as tf\n",
    "import time\n",
    "\n",
    "from reco_utils.common.constants import SEED\n",
    "from reco_utils.recommender.deeprec.deeprec_utils import (\n",
    "    prepare_hparams\n",
    ")\n",
    "from reco_utils.dataset.amazon_reviews import download_and_extract, data_preprocessing\n",
    "from reco_utils.dataset.download_utils import maybe_download\n",
    "from reco_utils.recommender.deeprec.models.sequential.sli_rec import SLI_RECModel\n",
    "####  to use the other model, use one of the following lines:\n",
    "#from reco_utils.recommender.deeprec.models.sequential.asvd import A2SVDModel\n",
    "#from reco_utils.recommender.deeprec.models.sequential.caser import CaserModel\n",
    "#from reco_utils.recommender.deeprec.models.sequential.gru4rec import GRU4RecModel\n",
    "\n",
    "from reco_utils.recommender.deeprec.IO.sequential_iterator import SequentialIterator\n",
    "\n",
    "print(\"System version: {}\".format(sys.version))\n",
    "print(\"Tensorflow version: {}\".format(tf.__version__))\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Parameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "tags": [
     "parameters"
    ]
   },
   "outputs": [],
   "source": [
    "EPOCHS = 10\n",
    "BATCH_SIZE = 400\n",
    "RANDOM_SEED = SEED  # Set None for non-deterministic result\n",
    "yaml_file = '../../reco_utils/recommender/deeprec/config/sli_rec.yaml'\n",
    "data_path = os.path.join(\"..\", \"..\", \"tests\", \"resources\", \"deeprec\", \"slirec\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  1. Input data format\n",
    "The input data contains 8 columns, i.e.,   `<label> <user_id> <item_id> <category_id> <timestamp> <history_item_ids> <history_cateory_ids> <hitory_timestamp>`  columns are seperated by `\"\\t\"`.  item_id and category_id denote the target item and category, which means that for this instance, we want to guess whether user user_id will interact with item_id at timestamp. `<history_*>` columns record the user behavior list up to `<timestamp>`, elements are separated by commas.  `<label>` is a binary value with 1 for positive instances and 0 for negative instances.  One example for an instance is: \n",
    "\n",
    "`1       A1QQ86H5M2LVW2  B0059XTU1S      Movies  1377561600      B002ZG97WE,B004IK30PA,B000BNX3AU,B0017ANB08,B005LAIHW2  Movies,Movies,Movies,Movies,Movies   1304294400,1304812800,1315785600,1316304000,1356998400` \n",
    "\n",
    "Only the SLi_Rec model is time-aware. For the other models, you can just pad some meaningless timestamp in the data files to fill up the format, the models will ignore these columns.\n",
    "\n",
    "We use Softmax to the loss function. In training and evalution stage, we group 1 positive instance with num_ngs negative instances. Pair-wise ranking can be regarded as a special case of Softmax ranking, where num_ngs is set to 1. \n",
    "\n",
    "More specifically,  for training and evalation, you need to organize the data file such that each one positive instance is followd by num_ngs negative instances. Our program will take 1+num_ngs lines as a unit for Softmax calculation. num_ngs is a parameter you need to pass to the `prepare_hparams`, `fit` and `run_eval` function. `train_num_ngs` in `prepare_hparams` denotes the number of negative instances for training, where a recommended number is 4. `valid_num_ngs` and `num_ngs` in `fit` and `run_eval` denote the number in evalution. In evaluation, the model calculates metrics among the 1+num_ngs instances. For the `predict` function, since we only need to calcuate a socre for each individual instance, there is no need for num_ngs setting.  More details and examples will be provided in the following sections.\n",
    "\n",
    "For training stage, if you don't want to prepare negative instances, you can just provide positive instances and set the parameter `need_sample=True, train_num_ngs=train_num_ngs` for function `prepare_hparams`, our model will dynamicly sample `train_num_ngs` instances as negative samples in each mini batch.\n",
    "\n",
    "###  Amazon dataset\n",
    "Now let's start with a public dataset containing product reviews and metadata from Amazon, which is widely used as a benchmark dataset in recommemdation systems field."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████████████████████████████████████████████████████████████████████████| 692k/692k [02:17<00:00, 5.02kKB/s]\n",
      "100%|████████████████████████████████████████████████████████████████████████████| 97.5k/97.5k [00:24<00:00, 4.00kKB/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "start reviews preprocessing...\n",
      "start meta preprocessing...\n",
      "start create instances...\n",
      "creating item2cate dict\n",
      "getting sampled data...\n",
      "start data processing...\n",
      "data generating...\n",
      "vocab generating...\n",
      "start valid negative sampling\n",
      "start test negative sampling\n"
     ]
    }
   ],
   "source": [
    "\n",
    "# for test\n",
    "train_file = os.path.join(data_path, r'train_data')\n",
    "valid_file = os.path.join(data_path, r'valid_data')\n",
    "test_file = os.path.join(data_path, r'test_data')\n",
    "user_vocab = os.path.join(data_path, r'user_vocab.pkl')\n",
    "item_vocab = os.path.join(data_path, r'item_vocab.pkl')\n",
    "cate_vocab = os.path.join(data_path, r'category_vocab.pkl')\n",
    "output_file = os.path.join(data_path, r'output.txt')\n",
    "\n",
    "reviews_name = 'reviews_Movies_and_TV_5.json'\n",
    "meta_name = 'meta_Movies_and_TV.json'\n",
    "reviews_file = os.path.join(data_path, reviews_name)\n",
    "meta_file = os.path.join(data_path, meta_name)\n",
    "train_num_ngs = 4 # number of negative instances with a positive instance for training\n",
    "valid_num_ngs = 4 # number of negative instances with a positive instance for validation\n",
    "test_num_ngs = 9 # number of negative instances with a positive instance for testing\n",
    "sample_rate = 0.01 # sample a small item set for training and testing here for fast example\n",
    "\n",
    "input_files = [reviews_file, meta_file, train_file, valid_file, test_file, user_vocab, item_vocab, cate_vocab]\n",
    "\n",
    "if not os.path.exists(train_file):\n",
    "    download_and_extract(reviews_name, reviews_file)\n",
    "    download_and_extract(meta_name, meta_file)\n",
    "    data_preprocessing(*input_files, sample_rate=sample_rate, valid_num_ngs=valid_num_ngs, test_num_ngs=test_num_ngs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1.1 Prepare hyper-parameters\n",
    "prepare_hparams() will create a full set of hyper-parameters for model training, such as learning rate, feature number, and dropout ratio. We can put those parameters in a yaml file (a complete list of parameters can be found under our config folder) , or pass parameters as the function's parameters (which will overwrite yaml settings).\n",
    "\n",
    "Parameters hints: <br>\n",
    "`need_sample` controls whether to perform dynamic negative sampling in mini-batch. \n",
    "`train_num_ngs` indicates how many negative instances followed by one positive instances.  <br>\n",
    "Examples: <br>\n",
    "(1) `need_sample=True and train_num_ngs=4`:  There are only positive instances in your training file. Our model will dynamically sample 4 negative instances for each positive instances in mini-batch. Note that if need_sample is set to True, train_num_ngs should be greater than zero. <br>\n",
    "(2) `need_sample=False and train_num_ngs=4`: In your training file, each one positive line is followed by 4 negative lines. Note that if need_sample is set to False, you must provide a traiing file with negative instances, and train_num_ngs should match the number of negative number in your training file."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "hparams = prepare_hparams(yaml_file, \n",
    "                          embed_l2=0., \n",
    "                          layer_l2=0., \n",
    "                          learning_rate=0.001, \n",
    "                          epochs=EPOCHS,\n",
    "                          batch_size=BATCH_SIZE,\n",
    "                          show_step=20,\n",
    "                          MODEL_DIR=os.path.join(data_path, \"model/\"),\n",
    "                          SUMMARIES_DIR=os.path.join(data_path, \"summary/\"),\n",
    "                          user_vocab=user_vocab,\n",
    "                          item_vocab=item_vocab,\n",
    "                          cate_vocab=cate_vocab,\n",
    "                          need_sample=True,\n",
    "                          train_num_ngs=train_num_ngs, # provides the number of negative instances for each positive instance for loss computation.\n",
    "            )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1.2 Create data loader\n",
    "Designate a data iterator for the model. All our sequential models use SequentialIterator. \n",
    "data format is introduced aboved. \n",
    "\n",
    "<br>Validation and testing data are files after negative sampling offline with the number of `<num_ngs>` and `<test_num_ngs>`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "input_creator = SequentialIterator"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Create model\n",
    "When both hyper-parameters and data iterator are ready, we can create a model:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "model = SLI_RECModel(hparams, input_creator, seed=RANDOM_SEED)\n",
    "##  of course you can create models like ASVDModel, CaserModel and GRU4RecModel in the same manner\n",
    "\n",
    "\n",
    "## sometimes we don't want to train a model from scratch\n",
    "## then we can load a pre-trained model like this: \n",
    "#model.load_model(r'your_model_path')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's see what is the model's performance at this point (without starting training):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'auc': 0.5114, 'logloss': 0.6931, 'mean_mrr': 0.29, 'ndcg2': 0.4517, 'ndcg4': 0.4517, 'ndcg6': 0.4517, 'ndcg8': 0.4517, 'ndcg10': 0.4517, 'group_auc': 0.512}\n"
     ]
    }
   ],
   "source": [
    "print(model.run_eval(test_file, num_ngs=test_num_ngs)) # test_num_ngs is the number of negative lines after each positive line in your test_file"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "AUC=0.5 is a state of random guess. We can see that before training, the model behaves like random guessing.\n",
    "\n",
    "#### 2.1 Train model\n",
    "Next we want to train the model on a training set, and check the performance on a validation dataset. Training the model is as simple as a function call:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "step 20 , total_loss: 1.6097, data_loss: 1.6097\n",
      "step 40 , total_loss: 1.6087, data_loss: 1.6087\n",
      "eval valid at epoch 1: auc:0.4895,logloss:0.693,mean_mrr:0.4475,ndcg2:0.5827,ndcg4:0.5827,ndcg6:0.5827,ndcg8:0.5827,ndcg10:0.5827,group_auc:0.4907\n",
      "step 20 , total_loss: 1.6069, data_loss: 1.6069\n",
      "step 40 , total_loss: 1.4812, data_loss: 1.4812\n",
      "eval valid at epoch 2: auc:0.5625,logloss:0.6931,mean_mrr:0.4916,ndcg2:0.6164,ndcg4:0.6164,ndcg6:0.6164,ndcg8:0.6164,ndcg10:0.6164,group_auc:0.5422\n",
      "step 20 , total_loss: 1.4089, data_loss: 1.4089\n",
      "step 40 , total_loss: 1.3968, data_loss: 1.3968\n",
      "eval valid at epoch 3: auc:0.684,logloss:0.6957,mean_mrr:0.5984,ndcg2:0.6985,ndcg4:0.6985,ndcg6:0.6985,ndcg8:0.6985,ndcg10:0.6985,group_auc:0.6787\n",
      "step 20 , total_loss: 1.2920, data_loss: 1.2920\n",
      "step 40 , total_loss: 1.3227, data_loss: 1.3227\n",
      "eval valid at epoch 4: auc:0.6965,logloss:0.6827,mean_mrr:0.6145,ndcg2:0.7107,ndcg4:0.7107,ndcg6:0.7107,ndcg8:0.7107,ndcg10:0.7107,group_auc:0.6914\n",
      "step 20 , total_loss: 1.3205, data_loss: 1.3205\n",
      "step 40 , total_loss: 1.2936, data_loss: 1.2936\n",
      "eval valid at epoch 5: auc:0.6986,logloss:0.6657,mean_mrr:0.6192,ndcg2:0.7142,ndcg4:0.7142,ndcg6:0.7142,ndcg8:0.7142,ndcg10:0.7142,group_auc:0.6965\n",
      "step 20 , total_loss: 1.2575, data_loss: 1.2575\n",
      "step 40 , total_loss: 1.2785, data_loss: 1.2785\n",
      "eval valid at epoch 6: auc:0.7055,logloss:0.6147,mean_mrr:0.6197,ndcg2:0.7146,ndcg4:0.7146,ndcg6:0.7146,ndcg8:0.7146,ndcg10:0.7146,group_auc:0.699\n",
      "step 20 , total_loss: 1.2735, data_loss: 1.2735\n",
      "step 40 , total_loss: 1.2838, data_loss: 1.2838\n",
      "eval valid at epoch 7: auc:0.7205,logloss:0.6434,mean_mrr:0.6345,ndcg2:0.7257,ndcg4:0.7257,ndcg6:0.7257,ndcg8:0.7257,ndcg10:0.7257,group_auc:0.7092\n",
      "step 20 , total_loss: 1.1849, data_loss: 1.1849\n",
      "step 40 , total_loss: 1.1954, data_loss: 1.1954\n",
      "eval valid at epoch 8: auc:0.7234,logloss:0.6514,mean_mrr:0.6413,ndcg2:0.7308,ndcg4:0.7308,ndcg6:0.7308,ndcg8:0.7308,ndcg10:0.7308,group_auc:0.715\n",
      "step 20 , total_loss: 1.2023, data_loss: 1.2023\n",
      "step 40 , total_loss: 1.1818, data_loss: 1.1818\n",
      "eval valid at epoch 9: auc:0.7285,logloss:0.6794,mean_mrr:0.639,ndcg2:0.7292,ndcg4:0.7292,ndcg6:0.7292,ndcg8:0.7292,ndcg10:0.7292,group_auc:0.7152\n",
      "step 20 , total_loss: 1.1680, data_loss: 1.1680\n",
      "step 40 , total_loss: 1.1911, data_loss: 1.1911\n",
      "eval valid at epoch 10: auc:0.7317,logloss:0.6242,mean_mrr:0.6454,ndcg2:0.7339,ndcg4:0.7339,ndcg6:0.7339,ndcg8:0.7339,ndcg10:0.7339,group_auc:0.7181\n",
      "[(1, {'auc': 0.4895, 'logloss': 0.693, 'mean_mrr': 0.4475, 'ndcg2': 0.5827, 'ndcg4': 0.5827, 'ndcg6': 0.5827, 'ndcg8': 0.5827, 'ndcg10': 0.5827, 'group_auc': 0.4907}), (2, {'auc': 0.5625, 'logloss': 0.6931, 'mean_mrr': 0.4916, 'ndcg2': 0.6164, 'ndcg4': 0.6164, 'ndcg6': 0.6164, 'ndcg8': 0.6164, 'ndcg10': 0.6164, 'group_auc': 0.5422}), (3, {'auc': 0.684, 'logloss': 0.6957, 'mean_mrr': 0.5984, 'ndcg2': 0.6985, 'ndcg4': 0.6985, 'ndcg6': 0.6985, 'ndcg8': 0.6985, 'ndcg10': 0.6985, 'group_auc': 0.6787}), (4, {'auc': 0.6965, 'logloss': 0.6827, 'mean_mrr': 0.6145, 'ndcg2': 0.7107, 'ndcg4': 0.7107, 'ndcg6': 0.7107, 'ndcg8': 0.7107, 'ndcg10': 0.7107, 'group_auc': 0.6914}), (5, {'auc': 0.6986, 'logloss': 0.6657, 'mean_mrr': 0.6192, 'ndcg2': 0.7142, 'ndcg4': 0.7142, 'ndcg6': 0.7142, 'ndcg8': 0.7142, 'ndcg10': 0.7142, 'group_auc': 0.6965}), (6, {'auc': 0.7055, 'logloss': 0.6147, 'mean_mrr': 0.6197, 'ndcg2': 0.7146, 'ndcg4': 0.7146, 'ndcg6': 0.7146, 'ndcg8': 0.7146, 'ndcg10': 0.7146, 'group_auc': 0.699}), (7, {'auc': 0.7205, 'logloss': 0.6434, 'mean_mrr': 0.6345, 'ndcg2': 0.7257, 'ndcg4': 0.7257, 'ndcg6': 0.7257, 'ndcg8': 0.7257, 'ndcg10': 0.7257, 'group_auc': 0.7092}), (8, {'auc': 0.7234, 'logloss': 0.6514, 'mean_mrr': 0.6413, 'ndcg2': 0.7308, 'ndcg4': 0.7308, 'ndcg6': 0.7308, 'ndcg8': 0.7308, 'ndcg10': 0.7308, 'group_auc': 0.715}), (9, {'auc': 0.7285, 'logloss': 0.6794, 'mean_mrr': 0.639, 'ndcg2': 0.7292, 'ndcg4': 0.7292, 'ndcg6': 0.7292, 'ndcg8': 0.7292, 'ndcg10': 0.7292, 'group_auc': 0.7152}), (10, {'auc': 0.7317, 'logloss': 0.6242, 'mean_mrr': 0.6454, 'ndcg2': 0.7339, 'ndcg4': 0.7339, 'ndcg6': 0.7339, 'ndcg8': 0.7339, 'ndcg10': 0.7339, 'group_auc': 0.7181})]\n",
      "best epoch: 10\n",
      "Time cost for training is 9.53 mins\n"
     ]
    }
   ],
   "source": [
    "start_time = time.time()\n",
    "model = model.fit(train_file, valid_file, valid_num_ngs=valid_num_ngs) \n",
    "# valid_num_ngs is the number of negative lines after each positive line in your valid_file \n",
    "# we will evaluate the performance of model on valid_file every epoch\n",
    "end_time = time.time()\n",
    "print('Time cost for training is {0:.2f} mins'.format((end_time-start_time)/60.0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.2  Evaluate model\n",
    "\n",
    "Again, let's see what is the model's performance now (after training):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'auc': 0.7111, 'logloss': 0.6447, 'mean_mrr': 0.4673, 'ndcg2': 0.5934, 'ndcg4': 0.5934, 'ndcg6': 0.5934, 'ndcg8': 0.5934, 'ndcg10': 0.5934, 'group_auc': 0.698}\n"
     ]
    },
    {
     "data": {
      "application/papermill.record+json": {
       "res_syn": {
        "auc": 0.7111,
        "group_auc": 0.698,
        "logloss": 0.6447,
        "mean_mrr": 0.4673,
        "ndcg10": 0.5934,
        "ndcg2": 0.5934,
        "ndcg4": 0.5934,
        "ndcg6": 0.5934,
        "ndcg8": 0.5934
       }
      }
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "res_syn = model.run_eval(test_file, num_ngs=test_num_ngs)\n",
    "print(res_syn)\n",
    "pm.record(\"res_syn\", res_syn)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we want to get the full prediction scores rather than evaluation metrics, we can do this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = model.predict(test_file, output_file)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "# The data was downloaded in tmpdir folder. You can delete them manually if you do not need them any more."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.3  Running models with large dataset\n",
    "Here are performances using the whole amazon dataset among popular sequential models with 1,697,533 positive instances.\n",
    "<br>Settings for reproducing the results:\n",
    "<br>`learning_rate=0.001, dropout=0.3, item_embedding_dim=32, cate_embedding_dim=8, l2_norm=0, batch_size=400, \n",
    "train_num_ngs=4, valid_num_ngs=4, test_num_ngs=49`\n",
    "\n",
    "\n",
    "We compare the running time with CPU only and with GPU on the larger dataset. It appears that GPU can significantly accelerate the training. Hardware specification for running the large dataset: \n",
    "<br>GPU: Tesla P100-PCIE-16GB\n",
    "<br>CPU: 6 cores Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz\n",
    " \n",
    "| Models | AUC | g-AUC | NDCG@2 | NDCG@10 | seconds per epoch on GPU | seconds per epoch on CPU| config |\n",
    "| :------| :------: | :------: | :------: | :------: | :------: | :------: | :------ |\n",
    "| A2SVD | 0.8251 | 0.8178 | 0.2922 | 0.4264 | 249.5 | 440.0 | N/A |\n",
    "| GRU4Rec | 0.8411 | 0.8332 | 0.3213 | 0.4547 | 439.0 | 4285.0 | max_seq_length=50, hidden_size=40|\n",
    "| Caser | 0.8244 | 0.8171 | 0.283 | 0.4194 | 314.3 | 5369.9 | T=1, n_v=128, n_h=128, L=3, min_seq_length=5|\n",
    "| SLi_Rec | 0.8631 | 0.8519 | 0.3491 | 0.4842 | 549.6 | 5014.0 | attention_size=40, max_seq_length=50, hidden_size=40|\n",
    "\n",
    " Note that the four models are grid searched with a coarse granularity and the results are for reference only. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Reference\n",
    "\\[1\\] Zeping Yu, Jianxun Lian, Ahmad Mahmoody, Gongshen Liu, Xing Xie. Adaptive User Modeling with Long and Short-Term Preferences for Personailzed Recommendation. In Proceedings of the 28th International Joint Conferences on Artificial Intelligence, IJCAI’19, Pages 4213-4219. AAAI Press, 2019.\n",
    "\n",
    "\\[2\\] Balázs Hidasi, Alexandros Karatzoglou, Linas Baltrunas, Domonkos Tikk. Session-based Recommendations with Recurrent Neural Networks. ICLR (Poster) 2016\n",
    "\n",
    "\\[3\\] Tang, Jiaxi, and Ke Wang. Personalized top-n sequential recommendation via convolutional sequence embedding. Proceedings of the Eleventh ACM International Conference on Web Search and Data Mining. ACM, 2018."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": "Python (reco)",
   "language": "python",
   "name": "reco"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}